package com.fanly.weixin.netty.lock;

import com.xa.system.lock.decoder.DecoderHandler;
import com.xa.system.lock.encoder.EncoderHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
public class LockTcpServer {

	private int port = 8888;
	
	@PostConstruct
    public void startNetty() {
//        new Thread(() -> {
//            try {
//                this.init();
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//        }).start();
    }
	
	public static void main(String[] args) {
		new Thread(() -> {
            try {
                LockTcpServer tp = new LockTcpServer();
                tp.init();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
	}
	
    public  void init(){
        log.info("LockTcpServer-init 正在启动TCP服务器。。。。。。");
        System.out.println("LockTcpServer-init 正在启动TCP服务器。。。。。。");
        //主线程组
        NioEventLoopGroup boss = new NioEventLoopGroup();
        //工作线程组
        NioEventLoopGroup work = new NioEventLoopGroup();
        deviceChannelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
        try {
            //引导对象
            ServerBootstrap bootstrap = new ServerBootstrap();
            //绑定线程池：handler是针对bossGroup，childHandler是针对workerHandler
            //配置工作线程组
            bootstrap.group(boss,work);
            //配置为NIO的socket通道
            bootstrap.channel(NioServerSocketChannel.class);
            //绑定通道参数
            bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    //设置log监听器，并且日志级别为debug，方便观察运行流程
                    ch.pipeline().addLast("logging",new LoggingHandler("DEBUG"));
                    //编码器。发送消息时候用
                    ch.pipeline().addLast("encode",new EncoderHandler());
                    //解码器，接收消息时候用
                    ch.pipeline().addLast("decode",new DecoderHandler());
                    //业务处理类，最终的消息会在这个handler中进行业务处理
                    ch.pipeline().addLast("handler",new DecoderHandler());
                }
            });
            // 临时存放已完成三次握手的请求的队列的最大长度。
            // 如果未设置或所设置的值小于1，Java将使用默认值50。
            // 如果大于队列的最大长度，请求会被拒绝
            //缓冲区
            bootstrap.option(ChannelOption.SO_BACKLOG,20480);
            // 保持长连接状态
            //ChannelOption对象设置TCP套接字的参数，非必须步骤
            bootstrap.childOption(ChannelOption.SO_KEEPALIVE,true);
            bootstrap.childOption(ChannelOption.RCVBUF_ALLOCATOR,new FixedRecvByteBufAllocator(65535));
            bootstrap.childOption(ChannelOption.TCP_NODELAY,true);
            bootstrap.childOption(ChannelOption.SO_REUSEADDR,true);
            //使用了Future来启动线程，并绑定了端口
            ChannelFuture future = bootstrap.bind(port).sync();
            log.info("LockTcpServer-init 启动TCP服务器启动成功,正在监听端口:"+port);
            System.out.println("LockTcpServer-init 启动TCP服务器启动成功,正在监听端口:"+port);
            //该方法进行阻塞,等待服务端链路关闭之后继续执行。
            //这种模式一般都是使用Netty模块主动向服务端发送请求，然后最后结束才使用
            //以异步的方式关闭端口
            future.channel().closeFuture().sync();

        }catch (InterruptedException e) {
            log.info("LockTcpServer-init 启动TCP服务器启动异常，异常信息:{}",e.fillInStackTrace());
        }finally {
            //出现异常后，关闭线程组
            work.shutdownGracefully();
            boss.shutdownGracefully();
            log.info("LockTcpServer TCP服务器关闭成功");
        }
    }
    private static ChannelGroup deviceChannelGroup;
    private static Map<String, ChannelId> deviceMap = new ConcurrentHashMap<>();
    /**
     * 设备接入
     */
    public static void deviceAdd(Channel channel) {
        deviceChannelGroup.add(channel);
    }
    /**
     * 设备移除
     */
    public static void deviceRemove(Channel channel) {
        deviceChannelGroup.remove(channel);
        removeDeviceChannelId(channel.id());
    }

    public static ChannelGroup deviceChannelGroup() {
        return deviceChannelGroup;
    }

    public static void putDeviceChannelId(String code, ChannelId channelId) {
        deviceMap.put(code, channelId);
    }

    public static void removeDeviceChannelId(ChannelId channelId) {
        deviceMap.entrySet().removeIf(item -> item.getValue().equals(channelId));
    }

    public static ChannelId deviceChannelId(String code) {
        return deviceMap.getOrDefault(code, null);
    }

    public static Channel deviceChannel(ChannelId channelId){
        return deviceChannelGroup.find(channelId);
    }
    public static Map<String,ChannelId> deviceMap(){
        return deviceMap;
    }
    /**
     * DEVICE 操作 结束
     */
}
